/**  
 * 文件名:    IVRChannel.java  
 * 描述:   
 * 作者:      Sue
 * 版本:      1.0  
 * 创建时间:  2014-12-3 上午09:45:37  
 *  
 * 修改历史:  
 * 日期                          作者           版本         描述  
 * ------------------------------------------------------------------  
 * 2014-12-3     Sue     1.0    1.0 Version  
 */ 
package com.hp.bon.sgw.wh.channel;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URL;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import org.simpleframework.http.core.Container;
import org.simpleframework.http.core.ContainerProcessor;
import org.simpleframework.http.core.ContainerSelector;
import org.simpleframework.http.core.ContainerServer;
import org.simpleframework.transport.connect.Connection;
import org.simpleframework.transport.connect.SocketConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.bon.sgw.Constants.NodeStatus;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.core.BuzProcessorManager;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.core.channel.IChannel;
import com.hp.bon.sgw.domain.Node;
import com.hp.bon.sgw.domain.SGWChannel;
import com.hp.bon.sgw.service.ConfigListener;
import com.hp.bon.sgw.service.ConfigService;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.wh.Constants.IVRParam;
import com.hp.bon.sgw.wh.channel.handler.IVRHandler;

/**  
 * @ClassName: IVRChannel   
 * @Description: 10000IVR欠费停机和用户欠费查询  
 * @author: Sue  
 * @date:2014-12-3 上午09:45:37     
 */
public class IVRChannel implements Container, ConfigListener, IChannel
{
	
	private static final Logger LOGGER = LoggerFactory.getLogger(IVRChannel.class);
	
	private static XMLInputFactory inputFactory = XMLInputFactory.newInstance();
	
	private ContainerServer server = null;
	private SGWChannel channel;
	protected ConfigService confSrv;
	private MultiThreadedHttpConnectionManager connectionManager;
	private HttpClient httpClient;
	
	ThreadPoolExecutor executor;
	ThreadPoolExecutor collector;
	
	public IVRChannel(SGWChannel channel)
	{
		this.channel = channel;
		confSrv = SpringContext.getInstance().getConfigService();
	}
	
	@Override
	public void start() throws IOException 
	{
		LOGGER.info("start IVRChannel:" + channel.toString() + " ...");
		String key = IVRParam.SGW_THREAD_MAX;
		int maxThreads = confSrv.getIntValue(key, 20);
		LOGGER.info(key + " : " + maxThreads);
		String endPoint = this.channel.getUri();
		if (endPoint == null) {
			LOGGER.warn("channel dont't defined uri ...");
			return;
		}
		LOGGER.info("start sgw IVRChannel server portal at " + endPoint + " ...");

		try 
		{
			URL srvURL = new URL(endPoint);
			int port = srvURL.getPort();
			//找不到端口就启动失败
			if (port == -1) 
			{
				LOGGER.info("start IVRChannel error : port not defined ...");
				return ;
			}
			String host = srvURL.getHost();
			if (host.equals("127.0.0.1") || host.equalsIgnoreCase("localhost")) 
			{
				host = "0.0.0.0";
			}
			server = new ContainerServer(this, maxThreads);

			Connection connection = new SocketConnection(server);
			SocketAddress address = new InetSocketAddress(host, port);
			connection.connect(address);
			collector = ((ContainerSelector) ((ContainerProcessor) server.getProcessor()).getSelector()).getCollect().getQueue();
			executor = ((ContainerSelector) ((ContainerProcessor) server.getProcessor()).getSelector()).getExecutor().getQueue();
			connectionManager = new MultiThreadedHttpConnectionManager();
			// ... configure connectionManager ...
			httpClient = new HttpClient(connectionManager);
			httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(10000);
			httpClient.getHttpConnectionManager().getParams().setSoTimeout(45000);

			this.channel.setStatus(NodeStatus.ON_LINE);
			confSrv.updateChannelStatus2DB(this.channel.getId(),NodeStatus.ON_LINE);
		} catch (IOException e) 
		{
			LOGGER.error("start IVRChannel " + this.channel.toString()+ "  ; err : " + e);
		}
	}
	
	@Override
	public void stop() 
	{
		LOGGER.info("stop IVRChannel ...");
		if (server != null) 
		{
			try 
			{
				server.stop();
			} catch (IOException e) 
			{
				e.printStackTrace();
			}
		}
		if (connectionManager != null) 
		{
			connectionManager.shutdown();
		}

		this.channel.setStatus(NodeStatus.OFF_LINE);
		confSrv.updateChannelStatus2DB(this.channel.getId(),NodeStatus.OFF_LINE);
		
		try 
		{
			LOGGER.info("wait 3s to exit...");
			Thread.sleep(3*1000);
		} catch (InterruptedException e) 
		{
			LOGGER.warn("Thread.sleep InterruptedException:",e);
		}
	}
	
	private static String path = "ivr";//TODO确定发过来的地址

	@Override
	public void handle(Request request, Response response) 
	{
		LOGGER.info("receive request from " + request.getClientAddress() + "; "	+ request.toString());
		long receiveTime = System.currentTimeMillis();
		PrintStream body = null;
		
		try 
		{
			body = response.getPrintStream();
			response.set("Content-Type", "text/xml;charset=UTF-8");
			response.set("Server", "HP Asyn Server )");
			response.setDate("Date", receiveTime);
			response.setDate("Last-Modified", receiveTime);

			if (!request.getPath().getPath().endsWith(path)) 
			{
				LOGGER.debug("request path is invalid:"	+ request.getPath().getPath()+ " , ignore this request.");
				return;
			}

			// TODO 增加IP校验
			String IP = request.getClientAddress().getAddress().getHostAddress();
			
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(request.getInputStream(),"utf-8")); 
	        StringBuffer stringBuffer =new StringBuffer("");
	        String temp;
	        while((temp=bufferedReader.readLine())!=null)
	        {
	            stringBuffer.append(temp);
	        }
	        bufferedReader.close();
	        String reqXml = stringBuffer.toString();
	        LOGGER.info("request from taobao : "+reqXml);
	        
	        LinkedHashMap<String ,String> reqMap ;
	        String scid = "";
	        reqMap = parseWSRequest(reqXml);
	        
	        if(reqMap == null){
	        	return ;
	        }
	        
	        scid = reqMap.get(IVRParam.ELEMENT_SCID);
	        if(scid.equals(""))
	        {
	        	creatErrAnswer(body ,2001 ,"" ,reqMap.get(IVRParam.ELEMENT_MID));// TODO 格式异常
	        	return;
	        }
	        
	        XmlMessage xmlMessage = null ;
	        if(scid.equals(IVRParam.BLOCKQUERY_HB))//停机状态查询
	        {
	        	xmlMessage = directXmlMessage(reqMap ,IVRParam.BLOCKQUERY_HB ,IP ,receiveTime);
	        }else if(scid.equals(IVRParam.BILLQUERYBYACCTID_PAYMENT))//账户欠费查询
	        {
	        	xmlMessage = directXmlMessage(reqMap ,IVRParam.BILLQUERYBYACCTID_PAYMENT ,IP ,receiveTime);
	        }else//能力不存在
	        {
	        	creatErrAnswer(body ,2001 ,"" ,reqMap.get(IVRParam.ELEMENT_MID));// TODO 格式异常
	        	return;
	        }
	        
	        // new CallParam对象
			IVRHandler handler = new IVRHandler(body);
			CallParam callParam = new CallParam(xmlMessage.getMID(), xmlMessage.getSCID(),xmlMessage, xmlMessage.getUID(), handler);
			BuzProcessorManager.getInstance().processRequest(callParam);

		} catch (Exception e) {
			LOGGER.warn("Exception:"+e);
		}
	}

	/**
	 * 
	 * @MethodName: directXmlMessage   
	 * @Description: 根据reqMap构造一个XmlMessage对象
	 * @param @param reqMap
	 * @param @param serviceContextId
	 * @param @param ip
	 * @param @param receiveTime
	 * @return XmlMessage  
	 * @throws
	 */
	private XmlMessage directXmlMessage(LinkedHashMap<String, String> reqMap ,String serviceContextId ,String ip ,long receiveTime) 
	{
		String mid = reqMap.get(IVRParam.ELEMENT_MID);
		String uid = reqMap.get(IVRParam.ELEMENT_UID);
		//String pwd = reqMap.get(IVRParam.ELEMENT_PWD);
		String scid = reqMap.get(IVRParam.ELEMENT_SCID);
		String areaCode = reqMap.get(IVRParam.ELEMENT_AREA_CODE);
		String accNbr = reqMap.get(IVRParam.ELEMENT_ACC_NBR);
		String startMonth = reqMap.get(IVRParam.ELEMENT_START_MONTH);
		String endMonth = reqMap.get(IVRParam.ELEMENT_END_MONTH);
		String option = reqMap.get(IVRParam.ELEMENT_OPTION);
		
		String bankCode = reqMap.get(IVRParam.ELEMENT_BANK_CODE);
		String billQueryType = reqMap.get(IVRParam.ELEMENT_BILLQUERY_TYPE);
		String destinationAccount = reqMap.get(IVRParam.ELEMENT_DESTINATION_ACCOUNT);
		String destinationAttr = reqMap.get(IVRParam.ELEMENT_DESTINATION_ATTR);
		String queryFlag = reqMap.get(IVRParam.ELEMENT_QUERY_FLAG);
		String feeQueryFlag = reqMap.get(IVRParam.ELEMENT_FEE_QUERY_FLAG);
		
		Element serviceInformation = DocumentHelper.createElement("Service_Information");
		Element element = null; 
		
		if(serviceContextId.equals(IVRParam.BLOCKQUERY_HB))//停机状态查询
		{
			if (areaCode != null) 
			{
				element = serviceInformation.addElement(IVRParam.ELEMENT_AREA_CODE);
				element.setText(areaCode);
			}
			if (accNbr != null) 
			{
				element = serviceInformation.addElement(IVRParam.ELEMENT_ACC_NBR);
				element.setText(accNbr);
			}
			if (startMonth != null) 
			{
				element = serviceInformation.addElement(IVRParam.ELEMENT_START_MONTH);
				element.setText(startMonth);
			}
			if (endMonth != null) 
			{
				element = serviceInformation.addElement(IVRParam.ELEMENT_END_MONTH);
				element.setText(endMonth);
			}
			if (option != null) 
			{
				element = serviceInformation.addElement(IVRParam.ELEMENT_OPTION);
				element.setText(option);
			}
		}else if(serviceContextId.equals(IVRParam.BILLQUERYBYACCTID_PAYMENT))//账户欠费查询
		{
			if (areaCode != null) 
			{
				element = serviceInformation.addElement(IVRParam.ELEMENT_AREA_CODE);
				element.setText(areaCode);
			}
			if (bankCode != null) 
			{
				element = serviceInformation.addElement(IVRParam.ELEMENT_BANK_CODE);
				element.setText(bankCode);
			}
			
			if(billQueryType != null || destinationAccount != null || destinationAttr != null || queryFlag != null || feeQueryFlag != null)
			{
				Element billQuery = serviceInformation.addElement(IVRParam.ELEMENT_BILL_QUERY);
				
				if (billQueryType != null) 
				{
					element = billQuery.addElement(IVRParam.ELEMENT_BILLQUERY_TYPE);
					element.setText(billQueryType);
				}
				if (destinationAccount != null) 
				{
					element = billQuery.addElement(IVRParam.ELEMENT_DESTINATION_ACCOUNT);
					element.setText(destinationAccount);
				}
				if (destinationAttr != null) 
				{
					element = billQuery.addElement(IVRParam.ELEMENT_DESTINATION_ATTR);
					element.setText(destinationAttr);
				}
				if (queryFlag != null) 
				{
					element = billQuery.addElement(IVRParam.ELEMENT_QUERY_FLAG);
					element.setText(queryFlag);
				}
				if (feeQueryFlag != null) 
				{
					element = billQuery.addElement(IVRParam.ELEMENT_FEE_QUERY_FLAG);
					element.setText(feeQueryFlag);
				}
			}
		}
		 
		
		// 将请求消息转换成DCC消息对应格式的XmlMessage
		XmlMessage req = new XmlMessage(true);

		req.setIp(ip);
		req.setMsgTime(receiveTime);
		req.setMID(mid);
		req.setUID(uid);
		req.setSCID(scid);
		req.setSIG(null);
		req.setREPTAG(0);
		req.setDATA(serviceInformation.asXML());
		req.setDataElement(serviceInformation);
		
		return req ;
	}

	/**
	 * 
	 * @MethodName: parseWSRequest   
	 * @Description: 将请求报文中的标签取出存入Map<标签Name，Value>
	 * 				注:根据需求只取SERVICE或者Bill_Query节点下的标签
	 * @param @param representation
	 * @param @throws Exception  
	 * @return LinkedHashMap<String,String>  
	 * @throws
	 */
	private static LinkedHashMap<String ,String> parseWSRequest(String representation) throws Exception 
	{
		InputStream input = new ByteArrayInputStream(representation.getBytes("UTF-8"));
		XMLStreamReader xmlStreamReader = inputFactory.createXMLStreamReader(input);
		LinkedHashMap<String ,String> req = new LinkedHashMap<String ,String>();
		int nextType = 0;

		boolean seenServiceReq = false;
		while (xmlStreamReader.hasNext()) 
		{
			nextType = xmlStreamReader.next();
			if (nextType == XMLStreamReader.START_ELEMENT) 
			{
				String elName = xmlStreamReader.getLocalName();
				if (elName.equals(IVRParam.ELEMENT_SERVICE) || elName.equals(IVRParam.ELEMENT_BILL_QUERY)) 
				{
					seenServiceReq = true;
				} 
				else if (seenServiceReq) 
				{
					String value = xmlStreamReader.getElementText();
					//System.out.println(elName + ":" + value);
					req.put(elName, value);
				}
			}
		}
		return req;
	}
	
	/**
	 * @throws IOException 
	 * @throws UnsupportedEncodingException 
	 * 
	 * @MethodName: creatErrAnswer   
	 * @Description: 构造异常信息返回  
	 * @param @param body
	 * @param @param retCode
	 * @param @param errMsg
	 * @param @param mid  
	 * @return void  
	 * @throws
	 */
	private void creatErrAnswer(PrintStream responseStream, int retCode ,String errMsg ,String mid) throws UnsupportedEncodingException, IOException 
	{
		StringBuilder sb = new StringBuilder();
		sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>")
			.append("<SERVICE>")
			.append("<MID>").append(mid).append("</MID>")
			.append("<RC>").append(retCode).append("</RC>")
			.append("</SERVICE>");
		
		responseStream.write(sb.toString().getBytes("UTF-8"));
		responseStream.close();
		
		if (LOGGER.isInfoEnabled()) {
			LOGGER.info("http response  " + sb.toString());
		}
	}
	
	@Override
	public Set<String> monitorConfKeys() 
	{
		return null;
	}

	@Override
	public void onConfigUpdated(String arg0) 
	{
		// TODO Auto-generated method stub
		
	}

	@Override
	public void addPersist(Node arg0) 
	{
		// TODO Auto-generated method stub
		
	}

	@Override
	public void deletePersist(Node arg0) 
	{
		// TODO Auto-generated method stub
		
	}

	@Override
	public int getNodeSendQueueSize(String arg0) 
	{
		return 0;
	}

	@Override
	public SGWChannel getSGWChannel() 
	{
		return this.channel;
	}

	@Override
	public int getThreadPoolActiveCount() 
	{
		return executor.getActiveCount();
	}

	@Override
	public int getThreadPoolQueueSize() 
	{
		return executor.getQueue().size();
	}

	@Override
	public int getThreadPoolSize() 
	{
		return executor.getPoolSize();
	}

	@Override
	public int sendRequest(CallParam arg0) 
	{
		return 0;
	}
	
	public static void main(String[] args) {
		String s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><SERVICE><MID>10000@027.ctnbc-bon.net;20120810152916;000004</MID>" +
				"<UID>10000@027.ctnbc-bon.net</UID><PWD>E807F1FCF82D132F9BB018CA6738A19F</PWD><SCID>BillQueryByAcctID.Payment@027.ctnbc-bon.net</SCID>" +
				"<Bank_code></Bank_code><Area_Code>0710</Area_Code><Bill_Query><BillQuery_Type>2</BillQuery_Type>" +
				"<Destination_Account>18986137973</Destination_Account><Destination_Attr>0</Destination_Attr><Query_Flag>0</Query_Flag>" +
				"<Fee_Query_Flag>0</Fee_Query_Flag></Bill_Query></SERVICE>";
		try {
			LinkedHashMap<String ,String> map = parseWSRequest(s);
			
			System.out.println("==============================" + map.get("aa") );//null
			
			Set<String> keySet = map.keySet();
			Iterator<String> it = keySet.iterator() ;
			while(it.hasNext()){
				String key = it.next() ;
				String value = map.get(key);
				System.out.println(key+":"+value);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
